import com.alibaba.fastjson.JSON;
import org.dom4j.*;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import xsd.domain.*;
import xsd.domain.Attribute;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by donar on 17/3/10.
 */
public class MWSXsd2JSON {

    // 默认数据节点，设置为空则认为根目录为默认数据节点  
    public static final String MESSAGE = "Features";

    //xml编码   
    public static final String ENCODING = "UTF-8";

    // xsd复合类型节点   
    public static final String COMPLEX_TYPE = "complexType";

    // xsd序列节点   
    public static final String SEQUENCE = "sequence";

    // xsd元素节点   
    public static final String ELEMENT = "element";


    // xsd注解文档节点   
    public static final String DOCUMENTATION = "documentation";

    // xsd简单类型节点   
    public static final String SIMPLE_TYPE = "simpleType";

    // xsd限制节点   
    public static final String RESTRICTION = "restriction";

    // xsd name属性   
    public static final String NAME = "name";

    // xsd type属性   
    public static final String TYPE = "type";

    // xsd base属性   
    public static final String BASE = "base";

    public static final String ENUMERATION = "enumeration";

    public static final String CHOICE = "choice";

    public static final String UNION = "union";


    Map<String, SimpleType> simpleTypeMap = new HashMap<String, SimpleType>();
    Map<String, ComplexType> complexTypeMap = new HashMap<String, ComplexType>();
    Map<String, ElementNode> elementMap = new HashMap<String, ElementNode>();
    List<SimpleType> simpleTypes = new ArrayList<SimpleType>();
    List<ComplexType> complexTypeList = new ArrayList<ComplexType>();
    List<ElementNode> eleNodeList = new ArrayList<ElementNode>();

    public static void main(String[] args) throws IOException, DocumentException {
        new MWSXsd2JSON().start();
    }
    public  void start() throws DocumentException, IOException {
        String baseXsdPath = "src/main/resources/";
        String[] xsdNames = new String[]{"amzn-base.xsd","Baby.xsd","Beauty.xsd","CE.xsd","ClothingAccessories.xsd","Gourmet.xsd","Health.xsd","Jewelry.xsd","LargeAppliances.xsd","Lighting.xsd","Sports.xsd","Tools.xsd","Toys.xsd","Wireless.xsd"};
        for(String name:xsdNames){
            parseXsd(baseXsdPath+name);
        }

//        //处理complex中的ref
//        for (ComplexType complexType:complexTypeList){
//            List<ElementNode> sq =complexType.getSequence();
//            if(sq!=null){
//                for (int i = 0; i < sq.size(); i++) {
//                    ElementNode elementNode= sq.get(i);
//                    if(elementNode.getRef()!=null){
//                        sq.set(i,elementMap.get(elementNode.getRef()));
//                    }
//                    if(elementNode.getTypeRef()!=null)elementNode.setType(getTypeInMap(elementNode.getTypeRef()));
//                }
//
//            }
//            List<ElementNode> choices =complexType.getChoice();
//            if(choices!=null){
//                for (int i = 0; i < choices.size(); i++) {
//                    ElementNode elementNode= choices.get(i);
//                    if(elementNode.getRef()!=null)sq.set(i,elementMap.get(elementNode.getRef()));
//                    if(elementNode.getTypeRef()!=null)elementNode.setType(getTypeInMap(elementNode.getTypeRef()));
//                }
//            }
//            List<Attribute> attributes =complexType.getAttributes();
//            if(attributes!=null){
//                for (int i = 0; i < attributes.size(); i++) {
//                    Attribute attribute= attributes.get(i);
//                    if(attribute.getTypeRef()!=null)attribute.setType(getTypeInMap(attribute.getTypeRef()));
//                }
//            }
//        }
        //处理element的ref 最外层的element 不会有ref 只会有typeRef
        for(ElementNode ele:eleNodeList){
            handleRef(ele);
        }



        //print
        for (ElementNode elementNode :eleNodeList){
            System.out.println();
            System.out.println(JSON.toJSONString(elementNode));
        }
        //到这里就全部解析完了 接下来要将ref处理了

    }

    private void handleRef(ElementNode ele) {
        if(ele.getTypeRef()!=null)ele.setInclude(getTypeInMap(ele.getTypeRef()));
        ele.setTypeRef(null);
        if(ele.getRef()!=null){
            copyFromRef(ele,elementMap.get(ele.getRef()));
            if(ele.getTypeRef()!=null)ele.setInclude(getTypeInMap(ele.getTypeRef()));
            ele.setTypeRef(null);
        }
        if(ele.getInclude() !=null && ele.getInclude() instanceof ComplexType ){
            ComplexType complexType = (ComplexType) ele.getInclude();
            List<ElementNode> sq =complexType.getSequence();
            if(sq!=null){
                for (int i = 0; i < sq.size(); i++) {
                    ElementNode elementNode= sq.get(i);
                    handleRef(elementNode);
                }
            }
            List<ElementNode> choices =complexType.getChoice();
            if(choices!=null){
                for (int i = 0; i < choices.size(); i++) {
                    ElementNode elementNode= choices.get(i);
                    handleRef(elementNode);
                }
            }
            List<Attribute> attributes =complexType.getAttributes();
            if(attributes!=null){
                for (int i = 0; i < attributes.size(); i++) {
                    Attribute attribute= attributes.get(i);
                    if(attribute.getTypeRef()!=null)attribute.setType(getTypeInMap(attribute.getTypeRef()));
                }
            }

        }
    }

    private void copyFromRef(ElementNode ele, ElementNode elementNode) {
        if(elementNode!=null){
            ele.setName(elementNode.getName());
            ele.setInclude(elementNode.getInclude());
            ele.setTypeRef(elementNode.getTypeRef());
            ele.setMaxOccurs(elementNode.getMaxOccurs());
            ele.setMinOccur(elementNode.getMinOccur());
            ele.setRef(null);
        }
    }

    public void parseXsd(String xsdPath) throws DocumentException, IOException {
        //先解析 base xsd
        SAXReader reader = new SAXReader();
        //String content =Files.toString(new File(baseXsdPath), Charsets.UTF_8);
        Document document = reader.read(xsdPath);
        List<Element> elementList = document.getRootElement().elements();
        //优先处理Type
        List<Element> simpleTypesRestriction = new ArrayList<Element>();
        List<Element> simpleTypesUnion = new ArrayList<Element>();
        List<Element> complexTypes = new ArrayList<Element>();
        List<Element> elementNodes = new ArrayList<Element>();
        for (Element e : elementList) {
            if (e.getName().equals(SIMPLE_TYPE)) {
                if (e.element(UNION) != null) simpleTypesUnion.add(e);
                if (e.element(RESTRICTION) != null) simpleTypesRestriction.add(e);
            }
            if (e.getName().equals(COMPLEX_TYPE)) complexTypes.add(e);
            if (e.getName().equals(ELEMENT)) elementNodes.add(e);
        }
        //###################################################\\
        //      处理restriction SimpleType 并将处理结果存入map \\
        for (Element simpleType : simpleTypesRestriction) {
            SimpleType st = (SimpleType) parseNode(simpleType);
            simpleTypeMap.put(st.getName(), st);
            simpleTypes.add(st);
        }
        for (Element simpleType : simpleTypesUnion) {
            SimpleType st = (SimpleType) parseNode(simpleType);
            simpleTypeMap.put(st.getName(), st);
            simpleTypes.add(st);
        }
        for (SimpleType simpleType : simpleTypes) {
            if (simpleType.getType().equals(RESTRICTION)) {
                SimpleType baseType = simpleTypeMap.get(simpleType.getRestriction().getBase());
                if (baseType != null) {
                    copyRestrictionFromBaseType(baseType, simpleType);
                }
            }
            // System.out.println(JSON.toJSONString(simpleType));
        }

        //###################################################\\
        //处理complexType 并将结果放到complexTypeMap中
        for (Element complexType : complexTypes) {
            ComplexType ct = (ComplexType) parseNode(complexType);
            complexTypeMap.put(ct.getName(), ct);
            complexTypeList.add(ct);
            //System.out.println(JSON.toJSONString(ct));
        }



        for (Element elm : elementNodes) {
            ElementNode elementNode = (ElementNode) parseNode(elm);
            elementMap.put(elementNode.getName(), elementNode);
            eleNodeList.add(elementNode);
            //System.out.println(JSON.toJSONString(elementNode));
        }


    }

    private Type getTypeInMap(String typeRef) {
        Type  type = simpleTypeMap.get(typeRef);
        if(type==null) type = complexTypeMap.get(typeRef);
        return type;
    }

    private void copyRestrictionFromBaseType(SimpleType baseType, SimpleType simpleType) {
        if (baseType.getRestriction() != null) {
            String base = baseType.getRestriction().getBase();
            List<String> enlt = baseType.getRestriction().getEnumerationList();
            Integer maxLen = baseType.getRestriction().getMaxLength();
            Integer minLen = baseType.getRestriction().getMinLength();
            String pattern = baseType.getRestriction().getPattern();
            Integer frac = baseType.getRestriction().getFractionDigits();
            Integer total = baseType.getRestriction().getTotalDigits();
            if (base != null) simpleType.getRestriction().setBase(base);
            if (enlt != null) simpleType.getRestriction().setEnumerationList(enlt);
            if (maxLen != null) simpleType.getRestriction().setMaxLength(maxLen);
            if (minLen != null) simpleType.getRestriction().setMinLength(minLen);
            if (pattern != null) simpleType.getRestriction().setPattern(pattern);
            if (frac != null) simpleType.getRestriction().setFractionDigits(frac);
            if (total != null) simpleType.getRestriction().setTotalDigits(total);
        }
    }


    public Object parseNode(Element e) {
        String name = e.getName();
        //#################simpleType################
        if (name.equals(SIMPLE_TYPE)) {
            SimpleType simpleType = new SimpleType();
            simpleType.setName(e.attributeValue(NAME));
            Element restriction = e.element(RESTRICTION);
            if (restriction != null) {
                simpleType.setType(RESTRICTION);
                simpleType.setRestriction((Restriction) parseNode(restriction));
            }
            Element union = e.element(UNION);
            if (union != null) {
                simpleType.setType(UNION);
                simpleType.setUnion((List<SimpleType>) parseNode(union));
            }
            return simpleType;
        }
        if (name.equals(RESTRICTION)) {
            Restriction restriction = new Restriction();
            String base = e.attributeValue(BASE);
            if ("decimal".equals(base)) {
                restriction.setTotalDigits(Integer.parseInt(e.element("totalDigits").attributeValue("value")));
                restriction.setFractionDigits(Integer.parseInt(e.element("fractionDigits").attributeValue("value")));
                //maxInclusion minInclusion 先不管
            } else if (e.element(ENUMERATION) != null) {
//                base=ENUMERATION;
                List<Element> enumerations = e.elements(ENUMERATION);
                List<String> enumerationList = new ArrayList<String>();
                for (Element enu : enumerations) {
                    enumerationList.add(enu.attributeValue("value"));
                }
                restriction.setEnumerationList(enumerationList);
            } else {
                if (e.element("maxLength") != null)
                    restriction.setMaxLength(Integer.parseInt(e.element("maxLength").attributeValue("value")));
                if (e.element("minLength") != null)
                    restriction.setMinLength(Integer.parseInt(e.element("minLength").attributeValue("value")));
                if (e.element("pattern") != null) restriction.setPattern(e.element("pattern").attributeValue("value"));

            }
            restriction.setBase(base);
            return restriction;

        }
        if (e.getName().equals(UNION)) {
            List<SimpleType> simpleTypes = new ArrayList<SimpleType>();
            String memberTypes = e.attributeValue("memberTypes");
            String[] memberTypeArr = memberTypes.split(" ");
            for (String mt : memberTypeArr) {
                SimpleType st = simpleTypeMap.get(mt);
                if (st != null) simpleTypes.add(st);
            }
            return simpleTypes;
        }


        //#################complexType################
        if (e.getName().equals(COMPLEX_TYPE)) {
            ComplexType complexType = new ComplexType();
            complexType.setName(e.attributeValue(NAME));
            Element simpleContent = e.element("simpleContent");
            if (simpleContent != null) {
                complexType.setAttributes(new ArrayList<Attribute>());
                List<Element> attrs = simpleContent.element("extension").elements("attribute");
                for (Element attr : attrs) {
                    complexType.getAttributes().add((Attribute) parseNode(attr));
                }
            } else {
                List<Element> attrs = e.elements("attribute");
                if (attrs != null && attrs.size() > 0) {
                    complexType.setAttributes(new ArrayList<Attribute>());
                    for (Element attr : attrs) {
                        complexType.getAttributes().add((Attribute) parseNode(attr));
                    }
                }
                Element sequence = e.element(SEQUENCE);

                if (sequence != null) {
                    List<Element> elements = sequence.elements(ELEMENT);
                    complexType.setSequence(new ArrayList<ElementNode>());
                    for (Element elnode : elements) {
                        complexType.getSequence().add((ElementNode) parseNode(elnode));
                    }
                    //sequence中也有可能有choice
                    Element choicesInSequence = sequence.element(CHOICE);
                    if (choicesInSequence != null) {
                        complexType.setChoice(new ArrayList<ElementNode>());
                        List<Element> choiceElements = choicesInSequence.elements(ELEMENT);
                        for (Element elnode : choiceElements) {
                            complexType.getChoice().add((ElementNode) parseNode(elnode));
                        }
                    }
                }
                Element choices = e.element(CHOICE);
                if (choices != null) {
                    //complexType.setOrder(SEQUENCE);
                    List<Element> elements = choices.elements(ELEMENT);
                    complexType.setChoice(new ArrayList<ElementNode>());
                    for (Element elnode : elements) {
                        complexType.getChoice().add((ElementNode) parseNode(elnode));
                    }
                }
            }
            return complexType;

        }

        //#################element################
        if (e.getName().equals(ELEMENT)) {
            ElementNode elementNode = new ElementNode();
            elementNode.setName(e.attributeValue(NAME));
            elementNode.setRef(e.attributeValue("ref"));
            elementNode.setTypeRef(e.attributeValue(TYPE));
            elementNode.setMinOccur(e.attributeValue("minOccurs"));
            elementNode.setMaxOccurs(e.attributeValue("maxOccurs"));
            Element simpleType = e.element("simpleType");
            if (simpleType != null) elementNode.setInclude((Type) parseNode(simpleType));
            Element complexType = e.element("complexType");
            if (complexType != null) elementNode.setInclude((Type) parseNode(complexType));
            return elementNode;
        }
        //#################attribute################
        if (e.getName().equals("attribute")) {
            Attribute attr = new Attribute();
            attr.setName(e.attributeValue(NAME));
            attr.setTypeRef(e.attributeValue(TYPE));
            attr.setUse(e.attributeValue("use"));
            if(e.element("simpleType")!=null) attr.setType((Type) parseNode(e.element("simpleType")));
            if(e.element("complexType")!=null) attr.setType((Type) parseNode(e.element("complexType")));
            return attr;
        }
        return null;
    }


}
